package org.misty.redis.autoconfigure.locker;

import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.misty.redis.autoconfigure.common.MultiConfigProperties;
import org.misty.redis.autoconfigure.util.BeanUtil;
import org.misty.redis.autoconfigure.util.Util;
import org.misty.redis.client.RedisClient;
import org.misty.redis.locker.RedisLocker;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.boot.context.properties.bind.BindResult;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.StringUtils;

import java.util.Objects;

import static org.misty.redis.autoconfigure.common.Constants.CONFIGURATION_ROOT;
import static org.misty.redis.autoconfigure.common.Constants.PROPERTY_INSTANCE_DEFAULT;

/**
 * @author Misty on 2020-06-12
 */
@Slf4j
public class MultiLockerRegister implements ImportBeanDefinitionRegistrar, BeanFactoryAware, EnvironmentAware {
    @Setter
    private BeanFactory beanFactory;
    @Setter
    private Environment environment;

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        BindResult<MultiConfigProperties> bindResult = Binder.get(environment).bind(CONFIGURATION_ROOT, MultiConfigProperties.class);

        bindResult.ifBound(properties -> {
            properties.getLocker().forEach((instanceName, config) -> {
                if (!PROPERTY_INSTANCE_DEFAULT.equals(instanceName) && config.isEnabled()) {
                    registerBeanDefinition(registry, instanceName, config);
                }
            });
        });
    }

    private void registerBeanDefinition(BeanDefinitionRegistry registry, String instanceName, MultiConfigProperties.LockerConfig config) {
        String beanName = config.getBeanName();
        if (StringUtils.isEmpty(beanName)) {
            beanName = BeanUtil.getBeanName(Util.camelize(instanceName), LockerConstants.BEAN_NAME_SUFFIX);
        }
        BeanUtil.uniqueBeanDefinition(registry, beanName);

        GenericBeanDefinition definition = BeanUtil.singletonBeanDefinition(RedisLocker.class, config.isPrimary());
        definition.setDescription("Register by " + this.getClass().getName());
        definition.setInstanceSupplier(() -> {
            RedisClient redisClient;
            if (!StringUtils.isEmpty(config.getClientBean())) {
                redisClient = beanFactory.getBean(config.getClientBean(), RedisClient.class);
                Objects.requireNonNull(redisClient, "RedisClient not found with. Bean name: " + config.getClientBean());
            } else {
                redisClient = beanFactory.getBean(RedisClient.class);
            }
            return new RedisLocker(redisClient, config);
        });
        log.debug("Register RedisLocker BeanDefinition with name: {}", beanName);
        registry.registerBeanDefinition(beanName, definition);
    }
}
